using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using System.Xml;

namespace ThatsMeSDK.Core
{
	public static class ServiceHandler
	{
		/// <summary>
		/// Loads Data from the Web API
		/// </summary>
		internal static IDataProvider dataloader;
		
		/// <summary>
		/// Sets if data will be sent by POST
		/// </summary>
		internal static bool POSTRequest = false;
		
		/// <summary>
		/// Add additional parameter which will be sent on every request
		/// </summary>
		internal static string AdditionalParameter = "";
		
		/// <summary>
		/// ThatsMe Api Url Constant
		/// </summary>
		private const string ApiUrl = "http://thats-me.ch/api/";
		
		/// <summary>
		/// Initialize the SDK Core
		/// </summary>
		/// <param name="format">Api Data Format</param>
		public static void Initialize(DataFormat format)
		{
			if(format == DataFormat.Xml)
			{
				dataloader = new XmlDataProvider();
			}
			else if(format == DataFormat.Json)
			{
				dataloader = new JsonDataProvider();
			}
		}
		
		/// <summary>
		/// Initialize the SDK Core
		/// </summary>
		/// <param name="format">Api Data Format</param>
		/// <param name="postrequest">Defines if requests will be sent by POST (default = false)</param>
		public static void Initialize(DataFormat format, bool postrequest)
		{
			Initialize(format);
			POSTRequest = postrequest;
		}
		
		/// <summary>
		/// Initialize the SDK Core
		/// </summary>
		/// <param name="format">Api Data Format</param>
		/// <param name="postrequest">Defines if requests will be sent by POST (default = false)</param>
		/// <param name="AdditionalParamter">Add additional parameter which will be sent on every request.
		/// Example: "mobile=1&modus=debug"</param>
		public static void Initialize(DataFormat format, bool postrequest, string additionalparamter)
		{
			Initialize(format, postrequest);
			AdditionalParameter = additionalparamter;
		}
		
		/// <summary>
		/// Load informations about a member by his nickname.
		/// </summary>
		/// <param name="nick">Nickname of the member</param>
		/// <returns>Filled Member Object</returns>
		public static Member GetMemberByNick(string nick)
		{
			//Fill API Data
			ApiCall call = new ApiCall("getMemberByNick");
			call.Parameter.Add("nick", nick);
			call.PostRequest = POSTRequest;
			
			//Handle Request
			XmlDocument memberdata = CallNativeApiFunction(call);
			return ServiceFunctions.FillMemberData(memberdata.SelectSingleNode("member"));
		}

		/// <summary>
		/// Load informations about a member by his id.
		/// </summary>
		/// <param name="nick">Id of the member</param>
		/// <returns>Filled Member Object</returns>
		public static Member GetMemberById(int id)
		{
			//Fill API Data
			ApiCall call = new ApiCall("getMemberById");
			call.Parameter.Add("id", id.ToString());
			call.PostRequest = POSTRequest;
			
			//Handle Request
			XmlDocument memberdata = CallNativeApiFunction(call);
			return ServiceFunctions.FillMemberData(memberdata.SelectSingleNode("member"));
		}
		
		/// <summary>
		/// Checks if a user exists
		/// </summary>
		/// <param name="nick">Nick of the member</param>
		/// <returns>bool, if member exists</returns>
		public static Boolean CheckMemberByNick(string nick)
		{
			//Fill API Data
			ApiCall call = new ApiCall("getMemberByNick");
			call.Parameter.Add("nick", nick);
			call.PostRequest = POSTRequest;
			
			//Handle Request
			XmlDocument memberdata = CallNativeApiFunction(call);
			return memberdata.SelectSingleNode("member").HasChildNodes;
		} 
		
		/// <summary>
		/// Returns a random picture of TM
		/// </summary>
		/// <param name="token">User authentification token</param>
		/// <returns>GalleryPicture with filled informations</returns>
		public static GalleryPicture GetRandomPic(string token)
		{
			return GetRandomPic(token, null);
		}
		
		/// <summary>
		/// Returns a random picture of TM
		/// </summary>
		/// <param name="token">User authentification token</param>
		/// <param name="filter">Specify if just random pics from male or female returns</param>
		/// <returns>GalleryPicture with filled informations</returns>
		public static GalleryPicture GetRandomPic(string token, Gender? filter)
		{
			//Fill API Data
			ApiCall call = new ApiCall("getRandomPic");
			call.Parameter.Add("token", token);
			
			//check for gender
			if (filter != null)
			{
				call.Parameter.Add("gender", filter.ToString());
			}
			call.PostRequest = POSTRequest;
			
			//Handle Request
			XmlDocument memberdata = CallNativeApiFunction(call);
			return ServiceFunctions.FillGalleryPictureData(memberdata.SelectSingleNode("pic"));
		}
		
		/// <summary>
		/// Returns the news picture fo TM
		/// </summary>
		/// <param name="limit">Limit the amount of pictures</param>
		/// <returns>A list of Pictures</returns>
		public static List<GalleryPicture> GetNewPics(int limit)
		{
			List<GalleryPicture> pictures = new List<GalleryPicture>();
			
			//Fill API Data
			ApiCall call = new ApiCall("getNewPics");
			call.Parameter.Add("limit", limit.ToString());
			call.PostRequest = POSTRequest;
			
			//Handle Request
			XmlDocument picturedata = CallNativeApiFunction(call);
			XmlNode picturesNode = picturedata.SelectSingleNode("pics");
			
			foreach(XmlNode picNode in picturesNode.ChildNodes)
			{
				pictures.Add(ServiceFunctions.FillGalleryPictureData(picNode));
			}
			
			return pictures;
		}

		/// <summary>
		/// authenticate a member and creates a new session
		/// </summary>
		/// <param name="nick">nickname of the user</param>
		/// <param name="passwordhash">md5 claculated password hash of the user</param>
		/// <returns>token if login was ok
		/// null if login failed</returns>
		public static string Authenticate(string nick, string passwordhash)
		{
			string token = null;
			
			//Fill API Data
			ApiCall call = new ApiCall("authenticate");
			call.Parameter.Add("nick", nick);
			call.Parameter.Add("pass", passwordhash);
			call.PostRequest = POSTRequest;
			
			//Handle Request
			XmlDocument logindata = CallNativeApiFunction(call);
			XmlNode memberNode = logindata.SelectSingleNode("member");
			
			if (memberNode.ChildNodes.Count > 0)
			{
				//Login was Ok
				token = memberNode.SelectSingleNode("token").InnerText;
			}
			else
			{
				//Login failed
			}
			
			return token;
		}
		
		/// <summary>
		/// Returns a MessageCount Object which contains all data about the amount of messages
		/// </summary>
		/// <param name="token">User authentification token</param>
		/// <returns>MessageCount Object with the data, on error null</returns>
		public static MessageCount CountMessages(string token)
		{
			MessageCount counter = null;
			
			//Fill API Data
			ApiCall call = new ApiCall("countMessages");
			call.Parameter.Add("token", token);
			call.PostRequest = POSTRequest;
			
			//Handle Request
			XmlDocument counterdata = CallNativeApiFunction(call);
			
			if(!ServiceFunctions.CheckAuthentificationError(counterdata))
			{
				XmlNode countNode = counterdata.SelectSingleNode("count");
				XmlNode unreadNode = countNode.SelectSingleNode("unread");
				XmlNode inboxNode = countNode.SelectSingleNode("inbox");
				XmlNode outboxNode = countNode.SelectSingleNode("outbox");
				
				counter = new MessageCount();
				
				//Fill Data
				counter.Unread = new MessageCount.MessageCounter(
					Convert.ToInt32(unreadNode.SelectSingleNode("messages").InnerText),
					Convert.ToInt32(unreadNode.SelectSingleNode("notifications").InnerText),
					Convert.ToInt32(unreadNode.SelectSingleNode("total").InnerText));
				
				counter.Inbox = new MessageCount.MessageCounter(
					Convert.ToInt32(inboxNode.SelectSingleNode("messages").InnerText),
					Convert.ToInt32(inboxNode.SelectSingleNode("notifications").InnerText),
					Convert.ToInt32(inboxNode.SelectSingleNode("total").InnerText));
				
				counter.Outbox = new MessageCount.MessageCounter(
					Convert.ToInt32(outboxNode.SelectSingleNode("messages").InnerText),
					Convert.ToInt32(outboxNode.SelectSingleNode("notifications").InnerText),
					Convert.ToInt32(outboxNode.SelectSingleNode("total").InnerText));
			}
			
			return counter;
		}
		
		/// <summary>
		/// Loads a message by a message id
		/// </summary>
		/// <param name="id">Id of the message</param>
		/// <param name="token">User authentification token</param>
		/// <returns>Filled Message Object</returns>
		public static Message GetMessage(int id, string token)
		{
			Message message = null;
			
			//Fill API Data
			ApiCall call = new ApiCall("getMessage");
			call.Parameter.Add("token", token);
			call.Parameter.Add("id", id.ToString());
			call.PostRequest = POSTRequest;
			
			//Handle Request
			XmlDocument messageData = CallNativeApiFunction(call);
			
			if(!ServiceFunctions.CheckAuthentificationError(messageData))
			{
				message = ServiceFunctions.FillMessageData(messageData.SelectSingleNode("message"));
			}
			
			return message;
		}
		/// <summary>
		/// Loads a message by a message header
		/// </summary>
		/// <param name="messageHead">Message header of the message</param>
		/// <param name="token">User authentification token</param>
		/// <returns>Filled Message Object</returns>
		public static Message GetMessage(MessageHeader messageHead, string token)
		{
			return GetMessage(messageHead.Id, token);
		}
		
		/// <summary>
		/// Loads a list of messages
		/// </summary>
		/// <param name="token">User authentification token</param>
		/// <param name="start">Start of the message list</param>
		/// <param name="limit">List limit of item count</param>
		/// <param name="only">Message Type you want to load</param>
		/// <param name="fullMessage">Set true if you want to download full message</param>
		/// <returns></returns>
		public static List<Message> GetMessages(string token, int start, int limit, MessageType only, bool fullMessage)
		{
			List<Message> messages = null;
			
			//Fill API Data
			ApiCall call = new ApiCall("getMessages");
			call.Parameter.Add("token", token);
			call.Parameter.Add("start", start.ToString());
			call.Parameter.Add("limit", limit.ToString());
			call.Parameter.Add("full", Convert.ToInt32(fullMessage).ToString());
			call.PostRequest = POSTRequest;
			
			//Check Message Type
			if (only != MessageType.Both)
			{
				call.Parameter.Add("only", only.ToString().ToLower());
			}
			
			//Download an iterate
			XmlDocument messagesData = CallNativeApiFunction(call);
			
			messages = new List<Message>();
			XmlNode messagesNode = messagesData.SelectSingleNode("messages");
			
			foreach(XmlNode messageNode in messagesNode.ChildNodes)
			{
				Message tempMessage = new Message();
				if(fullMessage)
				{
					tempMessage = ServiceFunctions.FillMessageData(messageNode);
				}
				else
				{
					tempMessage.Header = ServiceFunctions.FillMessageHeaderData(messageNode);
				}
				messages.Add(tempMessage);
			}
			
			
			return messages;
		}
		
		/// <summary>
		/// Sends a message to the specified member (Doesn't work because of "Get" Request)
		/// </summary>
		/// <param name="token">User authentification token</param>
		/// <param name="body">Message Body</param>
		/// <param name="to">Message Recipient</param>
		/// <param name="subject">Message Subject</param>
		/// <returns>Boolean if success</returns>
		public static bool SendMessage (string token, string body, string to, string subject)
		{
			//Fill API Data
			ApiCall call = new ApiCall("sendMessage");
			call.Parameter.Add("token", token);
			call.Parameter.Add("body", body);
			call.Parameter.Add("to", to);
			call.Parameter.Add("subject", subject);
			call.PostRequest = POSTRequest;
			
			//Send message
			XmlDocument messageData = CallNativeApiFunction(call);
			
			XmlNode errorNode = messageData.SelectSingleNode("error");
			
			if (errorNode.InnerText == string.Empty)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		
		/// <summary>
		/// Loads the gallery categories of a member
		/// </summary>
		/// <param name="nick">Nickname of the member</param>
		/// <returns>List with GalleryCategories</returns>
		public static List<GalleryCategory> GetUserPicCategories(string nick)
		{
			List<GalleryCategory> categories = new List<GalleryCategory>();
			
			//Fill API Data
			ApiCall call = new ApiCall("getUserPicCategories");
			call.Parameter.Add("user", nick);
			call.PostRequest = POSTRequest;
			
			//Download an iterate
			XmlDocument categoriesData = CallNativeApiFunction(call);

			XmlNode categoriesNode = categoriesData.SelectSingleNode("categories");
			
			foreach(XmlNode categoryNode in categoriesNode)
			{
				categories.Add(ServiceFunctions.FilleGalleryCategoryData(nick, categoryNode));
			}
			
			return categories;
		}
		
		/// <summary>
		/// Loads all Pictures of a member by a gallery id
		/// </summary>
		/// <param name="nick">Nickname of the member</param>
		/// <param name="galleryid">Gallery Id</param>
		/// <returns>List with Gallery Pictures</returns>
		public static List<GalleryPicture> GetUserPics(string nick, int galleryid)
		{
			List<GalleryPicture> pictures = new List<GalleryPicture>();
			
			//Fill API Data
			ApiCall call = new ApiCall("getUserPics");
			call.Parameter.Add("user", nick);
			call.Parameter.Add("category", galleryid.ToString());
			call.PostRequest = POSTRequest;
			
			//Download an iterate
			XmlDocument picturesData = CallNativeApiFunction(call);

			XmlNode picturesNode = picturesData.SelectSingleNode("pics");
			
			foreach(XmlNode pictureNode in picturesNode)
			{
				pictures.Add(ServiceFunctions.FillGalleryPictureData(pictureNode));
			}
			
			return pictures;
		}
		
		/// <summary>
		/// Loads all Pictures of a member
		/// </summary>
		/// <param name="nick">Nickname of the member</param>
		/// <returns>List with Gallery Pictures</returns>
		public static List<GalleryPicture> GetUserPics(string nick)
		{
			return GetUserPics(nick, -1);
		}
		
		/// <summary>
		/// Returns a list of friends by a user
		/// </summary>
		/// <param name="nick">Nickname of the member</param>
		/// <returns>Returns a member which has filled the nickname, profileuri and profilepictureuri</returns>
		public static List<Member> GetFriends(string nick)
		{
			List<Member> friends = new List<Member>();
			
			//Fill API Data
			ApiCall call = new ApiCall("getFriends");
			call.Parameter.Add("user", nick);
			call.PostRequest = POSTRequest;
			
			//Download an iterate
			XmlDocument membersData = CallNativeApiFunction(call);
			XmlNode friendsNode = membersData.SelectSingleNode("friends");
			
			foreach(XmlNode memberNode in friendsNode)
			{
				Member member = new Member(memberNode.SelectSingleNode("nick").InnerText);
				member.ProfilePictureUri = new Uri(memberNode.SelectSingleNode("image").InnerText);
				member.ProfileUri = new Uri(memberNode.SelectSingleNode("profile").InnerText);
				
				friends.Add(member);
			}
			
			return friends;
		}
		
		/// <summary>
		/// Returns a list of friends by a user
		/// </summary>
		/// <param name="pic">Id of the picture</param>
		/// <returns>Returns a list of friends by a user</returns>
		public static List<PictureComment> GetPicComments(int pic)
		{
			return GetPicComments(pic, ElementOrder.DESC);
		}
		
		/// <summary>
		/// Returns a list of friends by a user
		/// </summary>
		/// <param name="pic">Id of the picture</param>
		/// <param name="order">List Order (ASC -> oldest first, DESC -> newest first) Default: DESC</param>
		/// <returns>Returns a list of friends by a user</returns>
		public static List<PictureComment> GetPicComments(int pic, ElementOrder order)
		{
			List<PictureComment> comments = new List<PictureComment>();
			
			//Fill API Data
			ApiCall call = new ApiCall("getPicComments");
			call.Parameter.Add("pic", pic.ToString());
			call.Parameter.Add("order", order.ToString());
			call.PostRequest = POSTRequest;
			
			//Download an iterate
			XmlDocument commentsData = CallNativeApiFunction(call);
			XmlNode commentsNode = commentsData.SelectSingleNode("comments");
			
			foreach(XmlNode commentNode in commentsNode)
			{
				XmlNode memberNode = commentNode.SelectSingleNode("from");
				
				//Create a member
				Member member = new Member(memberNode.SelectSingleNode("nick").InnerText);
				member.ProfilePictureUri = new Uri(memberNode.SelectSingleNode("image").InnerText);
				member.ProfileUri = new Uri(memberNode.SelectSingleNode("profile").InnerText);
				
				//Create a comment
				PictureComment comment = new PictureComment();
				comment.From = member;
				comment.Id = Convert.ToInt32(commentNode.SelectSingleNode("id").InnerText);
				comment.Text = commentNode.SelectSingleNode("text").InnerText;
				comment.Date = DateTime.Parse(commentNode.SelectSingleNode("date").InnerText);
				comment.SentFromMobile = ServiceFunctions.IntToBool(Convert.ToInt32(commentNode.SelectSingleNode("sentfrommobile").InnerText));
				
				comments.Add(comment);
			}
			
			return comments;
		}
		
		/// <summary>
		/// Adds a comment to a picture
		/// </summary>
		/// <param name="token"> Authentification Token authenticate</param>
		/// <param name="pic">Id of the picture</param>
		/// <param name="text">Text of the comment</param>
		/// <returns></returns>
		public static bool AddPicComment(string token, int pic, string text)
		{
			//Fill API Data
			ApiCall call = new ApiCall("addPicComment");
			call.Parameter.Add("token", token);
			call.Parameter.Add("pic", pic.ToString());
			call.Parameter.Add("text", text);
			call.PostRequest = POSTRequest;
			
			//Send comment
			XmlDocument commentData = CallNativeApiFunction(call);
			
			XmlNode errorNode = commentData.SelectSingleNode("error");
			
			if (errorNode.InnerText == string.Empty)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		
		/// <summary>
		/// Rates a picture
		/// </summary>
		/// <param name="token">Authentication Token authenticate</param>
		/// <param name="pic">Id of the picture</param>
		/// <param name="votevalue">Value of the vote</param>
		/// <returns></returns>
		public static bool AddPicRating(string token, int pic, VoteType votevalue)
		{
			//Fill API Data
			ApiCall call = new ApiCall("addPicRating");
			call.Parameter.Add("token", token);
			call.Parameter.Add("pic", pic.ToString());
			call.Parameter.Add("value", Convert.ToInt32(votevalue).ToString());
			call.PostRequest = POSTRequest;
			
			//Send vote
			XmlDocument voteData = CallNativeApiFunction(call);
			XmlNode errorNode = voteData.SelectSingleNode("error");
			
			if (errorNode.InnerText == string.Empty)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		public static  Member LoadCompletlyFilledMember(int id)
		{
			return LoadCompletlyFilledMember(GetMemberById(id).Nick);
		}

		public static  Member LoadCompletlyFilledMember(string nick)
		{
			Member member = GetMemberByNick(nick);
			//Here comes all the code which is needed to load a completly user.
			//Not like GetMemberByNick which filled only a couple of fields.    For example: Music,Friends,etc.

			return member;
		}

		public static Dashboard UpdateDashboardData(Dashboard dashboard)
		{
			dashboard.NewPictures = GetNewPics(1);
			//Here comes all Code to load the Dashboard completly like GetNewBlogs, etc.

			return null;
		}
		
		/// <summary>
		/// Calls the specified API Function
		/// </summary>
		/// <param name="functionName">Name of the API Function</param>
		/// <param name="parameter">Parameter String</param>
		/// <param name="postrequest">Checked if data should be sent by POST</param>
		/// <returns>Data as XmlDocument</returns>
		internal static XmlDocument CallNativeApiFunction(string functionName, string parameter, bool postrequest)
		{
			XmlDocument dataDoc;
			ServiceFunctions.CheckServiceHandlerReadyState();
			
			//Additional Parameter add
			if (AdditionalParameter != string.Empty)
			{
				parameter = parameter + "&" + AdditionalParameter;
			}
			
			if (postrequest)
			{
				dataDoc = dataloader.LoadDataByPost(new Uri(ApiUrl + functionName), UTF8Encoding.UTF8.GetBytes(parameter));
			}
			else
			{
				dataDoc = dataloader.LoadData(new Uri(ApiUrl + functionName + "?" + parameter));
			}
			
			return ServiceFunctions.CheckData(dataDoc);
		}
		
		/// <summary>
		/// Calls the specified API Function
		/// </summary>
		/// <param name="apiCall">API Call Object</param>
		/// <returns>Data as XmlDocument</returns>
		internal static XmlDocument CallNativeApiFunction(ApiCall apiCall)
		{
			string parameter = "";
			bool isFirstItem = true;
			string spacer = "";
			
			foreach(KeyValuePair<string, string> paramEntry in apiCall.Parameter)
			{
				parameter += spacer + paramEntry.Key + "=" + paramEntry.Value;
				
				if (isFirstItem)
				{
					isFirstItem = false;
					spacer += "&";
				}
			}
			
			return CallNativeApiFunction(apiCall.FunctionName, parameter, apiCall.PostRequest);
		}
		
		/// <summary>
		/// Calculates an MD5 hash of a text (string)
		/// </summary>
		/// <param name="Input">Text as string</param>
		/// <returns>Calculated Hash as string</returns>
		public static string CalculateMD5(string Input)
		{
			System.Security.Cryptography.MD5CryptoServiceProvider CryptoService;
			CryptoService = new System.Security.Cryptography.MD5CryptoServiceProvider();

			byte[] InputBytes = UTF8Encoding.UTF8.GetBytes(Input);
			InputBytes = CryptoService.ComputeHash(InputBytes);
			return BitConverter.ToString(InputBytes).Replace("-", "").ToLower();
		}
	}
}
